//===--- GenIntegerLiteral.cpp - IRGen for Builtin.IntegerLiteral ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements IR generation for Builtin.IntegerLiteral.
//
//===----------------------------------------------------------------------===//

#include "polarphp/irgen/internal/GenIntegerLiteral.h"

#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalVariable.h"
#include "polarphp/abi/MetadataValues.h"

#include "polarphp/irgen/internal/BitPatternBuilder.h"
#include "polarphp/irgen/internal/Explosion.h"
#include "polarphp/irgen/internal/ExtraInhabitants.h"
#include "polarphp/irgen/internal/GenType.h"
#include "polarphp/irgen/internal/IRGenFunction.h"
#include "polarphp/irgen/internal/IRGenModule.h"
#include "polarphp/irgen/internal/LoadableTypeInfo.h"
#include "polarphp/irgen/internal/ScalarPairTypeInfo.h"

using namespace polar;
using namespace irgen;

namespace {

/// A TypeInfo implementation for Builtin.IntegerLiteral.
class IntegerLiteralTypeInfo :
   public ScalarPairTypeInfo<IntegerLiteralTypeInfo, LoadableTypeInfo> {

public:
   IntegerLiteralTypeInfo(llvm::StructType *storageType,
                          Size size, Alignment align, SpareBitVector &&spareBits)
      : ScalarPairTypeInfo(storageType, size, std::move(spareBits), align,
                           IsPOD, IsFixedSize) {}

   static Size getFirstElementSize(IRGenModule &IGM) {
      return IGM.getPointerSize();
   }
   static StringRef getFirstElementLabel() {
      return ".data";
   }
   static bool isFirstElementTrivial() {
      return true;
   }
   void emitRetainFirstElement(IRGenFunction &IGF, llvm::Value *fn,
                               Optional<Atomicity> atomicity = None) const {}
   void emitReleaseFirstElement(IRGenFunction &IGF, llvm::Value *fn,
                                Optional<Atomicity> atomicity = None) const {}
   void emitAssignFirstElement(IRGenFunction &IGF, llvm::Value *fn,
                               Address fnAddr) const {
      IGF.Builder.CreateStore(fn, fnAddr);
   }

   static Size getSecondElementOffset(IRGenModule &IGM) {
      return IGM.getPointerSize();
   }
   static Size getSecondElementSize(IRGenModule &IGM) {
      return IGM.getPointerSize();
   }
   static StringRef getSecondElementLabel() {
      return ".flags";
   }
   bool isSecondElementTrivial() const {
      return true;
   }
   void emitRetainSecondElement(IRGenFunction &IGF, llvm::Value *data,
                                Optional<Atomicity> atomicity = None) const {}
   void emitReleaseSecondElement(IRGenFunction &IGF, llvm::Value *data,
                                 Optional<Atomicity> atomicity = None) const {}
   void emitAssignSecondElement(IRGenFunction &IGF, llvm::Value *context,
                                Address dataAddr) const {
      IGF.Builder.CreateStore(context, dataAddr);
   }

   // The data pointer isn't a heap object, but it is an aligned pointer.
   bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
      return true;
   }
   unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
      return getHeapObjectExtraInhabitantCount(IGM);
   }
   APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
                                      unsigned bits,
                                      unsigned index) const override {
      return getHeapObjectFixedExtraInhabitantValue(IGM, bits, index, 0);
   }
   llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src,
                                        PILType T,
                                        bool isOutlined) const override {
      src = projectFirstElement(IGF, src);
      return getHeapObjectExtraInhabitantIndex(IGF, src);
   }
   APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
      auto pointerSize = IGM.getPointerSize();
      auto mask = BitPatternBuilder(IGM.Triple.isLittleEndian());
      mask.appendSetBits(pointerSize.getValueInBits());
      mask.appendClearBits(pointerSize.getValueInBits());
      return mask.build().getValue();
   }
   void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index,
                             Address dest, PILType T,
                             bool isOutlined) const override {
      dest = projectFirstElement(IGF, dest);
      storeHeapObjectExtraInhabitant(IGF, index, dest);
   }
};

}

llvm::StructType *IRGenModule::getIntegerLiteralTy() {
   if (!IntegerLiteralTy) {
      IntegerLiteralTy =
         llvm::StructType::create(LLVMContext, {
            SizeTy->getPointerTo(),
            SizeTy
         }, "polarphp.int_literal");
   }
   return IntegerLiteralTy;
}

const LoadableTypeInfo &
TypeConverter::getIntegerLiteralTypeInfo() {
   if (!IntegerLiteralTI) {
      auto ty = IGM.getIntegerLiteralTy();

      SpareBitVector spareBits;
      spareBits.append(IGM.getHeapObjectSpareBits());
      spareBits.appendClearBits(IGM.getPointerSize().getValueInBits());

      IntegerLiteralTI =
         new IntegerLiteralTypeInfo(ty, IGM.getPointerSize() * 2,
                                    IGM.getPointerAlignment(),
                                    std::move(spareBits));
   }
   return *IntegerLiteralTI;
}

ConstantIntegerLiteral
irgen::emitConstantIntegerLiteral(IRGenModule &IGM, IntegerLiteralInst *ILI) {
   return IGM.getConstantIntegerLiteral(ILI->getValue());
}

ConstantIntegerLiteral
IRGenModule::getConstantIntegerLiteral(APInt value) {
   if (!ConstantIntegerLiterals)
      ConstantIntegerLiterals.reset(new ConstantIntegerLiteralMap());

   return ConstantIntegerLiterals->get(*this, std::move(value));
}

ConstantIntegerLiteral
ConstantIntegerLiteralMap::get(IRGenModule &IGM, APInt &&value) {
   auto &entry = map[value];
   if (entry.Data) return entry;

   assert(value.getMinSignedBits() == value.getBitWidth() &&
          "expected IntegerLiteral value to be maximally compact");

   // We're going to break the value down into pointer-sized chunks.
   uint64_t chunkSizeInBits = IGM.getPointerSize().getValueInBits();

   // Count how many bits are needed to store the value, including the sign bit.
   uint64_t minWidthInBits = value.getBitWidth();

   // Round up to the nearest multiple of the chunk size.
   uint64_t storageWidthInBits = (minWidthInBits + chunkSizeInBits - 1)
                                 & ~(chunkSizeInBits - 1);

   // Extend the value to that width.  We guarantee that extra bits in the
   // chunks will be appropriately sign-extended.
   value = value.sextOrTrunc(storageWidthInBits);

   // Extract the individual chunks from the extended value.
   uint64_t numChunks = storageWidthInBits / chunkSizeInBits;
   SmallVector<llvm::Constant *, 4> chunks;
   chunks.reserve(numChunks);
   for (uint64_t i = 0; i != numChunks; ++i) {
      auto chunk = value.extractBits(chunkSizeInBits, i * chunkSizeInBits);
      chunks.push_back(llvm::ConstantInt::get(IGM.SizeTy, std::move(chunk)));
   }

   // Build a global to hold the chunks.
   // TODO: make this shared within the image
   auto arrayTy = llvm::ArrayType::get(IGM.SizeTy, numChunks);
   auto initV = llvm::ConstantArray::get(arrayTy, chunks);
   auto globalArray =
      new llvm::GlobalVariable(*IGM.getModule(), arrayTy, /*constant*/ true,
                               llvm::GlobalVariable::PrivateLinkage, initV,
                               IGM.EnableValueNames
                               ? Twine("intliteral.") + value.toString(10, true)
                               : "");
   globalArray->setUnnamedAddr(llvm::GlobalVariable::UnnamedAddr::Global);

   // Various clients expect this to be a i64*, not an [N x i64]*, so cast down.
   auto zero = llvm::ConstantInt::get(IGM.Int32Ty, 0);
   llvm::Constant *indices[] = { zero, zero };
   auto data = llvm::ConstantExpr::getInBoundsGetElementPtr(arrayTy, globalArray,
                                                            indices);

   // Build the flags word.
   auto flags = IntegerLiteralFlags(minWidthInBits, value.isNegative());
   auto flagsV = llvm::ConstantInt::get(IGM.SizeTy, flags.getOpaqueValue());

   // Cache the global.
   entry.Data = data;
   entry.Flags = flagsV;
   return entry;
}

void irgen::emitIntegerLiteralCheckedTrunc(IRGenFunction &IGF,
                                           Explosion &in,
                                           llvm::IntegerType *resultTy,
                                           bool resultIsSigned,
                                           Explosion &out) {
   Address data(in.claimNext(), IGF.IGM.getPointerAlignment());
   auto flags = in.claimNext();

   size_t chunkWidth = IGF.IGM.getPointerSize().getValueInBits();
   size_t resultWidth = resultTy->getBitWidth();

   // The number of bits required to express the value, including the sign bit.
   auto valueWidth = IGF.Builder.CreateLShr(flags,
                                            IGF.IGM.getSize(Size(IntegerLiteralFlags::BitWidthShift)));

   // The maximum number of chunks that we need to read in order to fill the
   // result type: ceil(resultWidth / chunkWidth).
   // Note that we won't actually end up reading the final chunk if we're
   // building an unsigned value that requires e.g. 65 bits to express:
   // there's only one meaningful bit there, and we know it's zero from the
   // isNegative check.
   size_t maxNumChunks = (resultWidth + chunkWidth - 1) / chunkWidth;

   // One branch from invalidBB, one branch at each intermediate point in the
   // do-we-have-more-chunks chain, and one branch at the end.
   auto numPHIEntries = maxNumChunks + /*overflow*/ 1;

   auto boolTy = IGF.IGM.Int1Ty;
   auto doneBB = IGF.createBasicBlock("intliteral.trunc.done");
   auto resultPHI = llvm::PHINode::Create(resultTy, numPHIEntries, "", doneBB);
   auto overflowPHI = llvm::PHINode::Create(boolTy, numPHIEntries, "", doneBB);
   out.add(resultPHI);
   out.add(overflowPHI);

   auto validBB = IGF.createBasicBlock("intliteral.trunc.valid");
   auto invalidBB = IGF.createBasicBlock("intliteral.trunc.invalid");

   // Check whether the value fits in the result type.
   // If the result is signed, then we need valueWidth <= resultWidth.
   // Otherwise we need valueWidth <= resultWidth + 1 && !isNegative.
   {
      llvm::Value *hasOverflow;
      if (resultIsSigned) {
         hasOverflow = IGF.Builder.CreateICmpUGT(valueWidth,
                                                 IGF.IGM.getSize(Size(resultWidth)));
      } else {
         static_assert(IntegerLiteralFlags::IsNegativeFlag == 1,
                       "hardcoded in this truncation");
         auto isNegative = IGF.Builder.CreateTrunc(flags, boolTy);
         auto tooBig = IGF.Builder.CreateICmpUGT(valueWidth,
                                                 IGF.IGM.getSize(Size(resultWidth + 1)));
         hasOverflow = IGF.Builder.CreateOr(isNegative, tooBig);
      }
      IGF.Builder.CreateCondBr(hasOverflow, invalidBB, validBB);
   }

   // In the invalid block, we just need to construct the result.  This block
   // only exists to split the otherwise-critical edge.
   IGF.Builder.emitBlock(invalidBB);
   {
      resultPHI->addIncoming(llvm::ConstantInt::get(resultTy, 0), invalidBB);
      overflowPHI->addIncoming(llvm::ConstantInt::get(boolTy, 1), invalidBB);
      IGF.Builder.CreateBr(doneBB);
   }

   // Okay, the value fits in the result type, so overflow is off the table
   // and we just need to assemble a value of resultTy.  But we might not have
   // the full complement of chunks.
   IGF.Builder.emitBlock(validBB);
   {
      auto firstChunk = IGF.Builder.CreateLoad(data);

      // The easy case is if resultWidth <= chunkWidth, in which case knowing
      // that we haven't overflowed is sufficient to say that we can just
      // use the first chunk.
      if (resultWidth <= chunkWidth) {
         auto result = IGF.Builder.CreateTrunc(firstChunk, resultTy);
         resultPHI->addIncoming(result, validBB);
         overflowPHI->addIncoming(llvm::ConstantInt::get(boolTy, 0), validBB);
         IGF.Builder.CreateBr(doneBB);

         // Otherwise, we're going to have to test dynamically how many chunks
         // we need to read.
      } else {
         assert(maxNumChunks >= 2);
         llvm::Value *cur = firstChunk;
         for (size_t i = 1; i != maxNumChunks; ++i) {
            auto extendBB = IGF.createBasicBlock("intliteral.trunc.finish");
            auto nextBB = IGF.createBasicBlock("intliteral.trunc.next");

            // If the result is signed, then we're done if:
            //   valueWidth <= bitsInChunksReadSoFar
            // If the result is unsigned, then we're done if:
            //   valueWidth <= bitsInChunksReadSoFar + 1
            // (because we know the next bit will be zero)
            auto limit = i * chunkWidth + size_t(!resultIsSigned);
            auto isComplete =
               IGF.Builder.CreateICmpULE(valueWidth, IGF.IGM.getSize(Size(limit)));
            IGF.Builder.CreateCondBr(isComplete, extendBB, nextBB);

            // If we're done, extend the current value to the result type and
            // then branch out.
            IGF.Builder.emitBlock(extendBB);
            {
               auto extendedResult =
                  resultIsSigned ? IGF.Builder.CreateSExt(cur, resultTy)
                                 : IGF.Builder.CreateZExt(cur, resultTy);
               resultPHI->addIncoming(extendedResult, extendBB);
               overflowPHI->addIncoming(llvm::ConstantInt::get(boolTy, 0), extendBB);
               IGF.Builder.CreateBr(doneBB);
            }

            // Otherwise, load the next chunk.
            IGF.Builder.emitBlock(nextBB);
            auto nextChunkAddr =
               IGF.Builder.CreateConstArrayGEP(data, i, IGF.IGM.getPointerSize());
            auto nextChunk = IGF.Builder.CreateLoad(nextChunkAddr);

            // Zero-extend the current value and the chunk and then shift the
            // chunk into place.  If this is the last iteration, we should use
            // the final result type; the shift might then drop bits, but they
            // should just be sign-extension bits.
            auto nextTy = (i + 1 == maxNumChunks
                           ? resultTy
                           : llvm::IntegerType::get(IGF.IGM.getLLVMContext(),
                                                    (i + 1) * chunkWidth));
            cur = IGF.Builder.CreateZExt(cur, nextTy);
            auto shiftedNextChunk =
               IGF.Builder.CreateShl(IGF.Builder.CreateZExt(nextChunk, nextTy),
                                     i * chunkWidth);
            cur = IGF.Builder.CreateAdd(cur, shiftedNextChunk);
         }

         // Given the overflow check before, we know we don't need to look at
         // any more chunks.
         assert(cur->getType() == resultTy);
         auto curBB = IGF.Builder.GetInsertBlock();
         resultPHI->addIncoming(cur, curBB);
         overflowPHI->addIncoming(llvm::ConstantInt::get(boolTy, 0), curBB);
         IGF.Builder.CreateBr(doneBB);
      }
   }

   // Emit the continuation block.  We've already set up the PHIs here and
   // add them to `out`, so there's nothing else to do.
   IGF.Builder.emitBlock(doneBB);
}

static llvm::Value *emitIntegerLiteralToFloatCall(IRGenFunction &IGF,
                                                  llvm::Value *data,
                                                  llvm::Value *flags,
                                                  unsigned bitWidth) {
   assert(bitWidth == 32 || bitWidth == 64);
   auto fn = bitWidth == 32 ? IGF.IGM.getIntToFloat32Fn()
                            : IGF.IGM.getIntToFloat64Fn();
   auto call = IGF.Builder.CreateCall(fn, {data, flags});
   call->setCallingConv(IGF.IGM.SwiftCC);
   call->setDoesNotThrow();
   call->setOnlyReadsMemory();
   call->setOnlyAccessesArgMemory();

   return call;
}

llvm::Value *irgen::emitIntegerLiteralToFP(IRGenFunction &IGF,
                                           Explosion &in,
                                           llvm::Type *toType) {
   auto data = in.claimNext();
   auto flags = in.claimNext();

   assert(toType->isFloatingPointTy());
   switch (toType->getTypeID()) {
      case llvm::Type::HalfTyID: {
         auto flt = emitIntegerLiteralToFloatCall(IGF, data, flags, 32);
         return IGF.Builder.CreateFPTrunc(flt, toType);
      }

      case llvm::Type::FloatTyID:
         return emitIntegerLiteralToFloatCall(IGF, data, flags, 32);

      case llvm::Type::DoubleTyID:
         return emitIntegerLiteralToFloatCall(IGF, data, flags, 64);

         // TODO: add runtime functions for some of these?
      case llvm::Type::X86_FP80TyID:
      case llvm::Type::FP128TyID:
      case llvm::Type::PPC_FP128TyID: {
         auto dbl = emitIntegerLiteralToFloatCall(IGF, data, flags, 64);
         return IGF.Builder.CreateFPExt(dbl, toType);
      }

      default:
         llvm_unreachable("not a floating-point type");
   }
}
